﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using EFCore.BulkExtensions;
using Library.Common.Reflection;
using Library.Common.Serialize;
using Library.Core.Application.IInfrastructure;
using Library.Core.Domain.Aggregation;
using Library.Core.Domain.Repository;
using Library.Core.Domain.Specification;
using Library.Core.Infrastructure.EntityFrameworkDataAcceSynthetic.Extension;
using Library.Core.Infrastructure.PersistenceObject;
using Microsoft.EntityFrameworkCore;

namespace Library.Core.Infrastructure.EntityFrameworkDataAccess
{

    public enum OrderEnum
    {
        OrderBy,
        ThenBy,
        OrderByDescending,
        ThenByDescending,
    }
    /// <summary>
    /// 注意：在事务上下文的查询需要传递 readOnly = false
    /// </summary>
    /// <typeparam name="TDo"></typeparam>
    /// <typeparam name="TPo"></typeparam>
    public abstract class RepositoryBase<TDo, TPo> : IRepository<TDo>
        where TDo : Entity, new()
        where TPo : PersistenceObjectBase, new()
    {
        public readonly DbContext _readonlyContext;
        public readonly DbContext _readwriteContext;
        private ICurrentUserInfo _currentUserInfo;

        protected RepositoryBase(ICurrentUserInfo currentUserInfo, IEFContext dbContext)
        {
            _readonlyContext = dbContext.Get().First();
            _readwriteContext = dbContext.Get().Last();
            this._currentUserInfo = currentUserInfo;
        }
        /// <summary>
        /// 新增对象
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public virtual bool Add(TDo t, bool trackCurUser = true)
        {
            var po = GetPersistentObject(t, trackCurUser == false ? (bool?)null : true);
            _readwriteContext.Set<TPo>().Add(po);
            return true;
        }
        /// <summary>
        /// 单条插入
        /// </summary>
        /// <param name="t"></param>
        /// <param name="trackCurUser"></param>
        /// <returns></returns>
        public virtual async Task<bool> AddAsync(TDo t, bool trackCurUser = true)
        {
            var po = GetPersistentObject(t, trackCurUser == false ? (bool?)null : true);
            try
            {
                await _readwriteContext.Set<TPo>().AddAsync(po);
            }
            catch (Exception ex)
            {
                throw ex;
            }
           
            return true;
        }

        /// <summary>
        /// 同步批量插入
        /// </summary>
        /// <param name="list"></param>
        /// <param name="bulkInsert"></param>
        /// <param name="trackCurUser"></param>
        /// <returns></returns>
        public virtual bool AddRange(List<TDo> list, bool bulkInsert = false, bool trackCurUser = true)
        {
            var poList = GetPersistentObjectList(list, trackCurUser == false ? (bool?)null : true);
            if (bulkInsert)
                _readwriteContext.BulkInsert(poList);
            else
                _readwriteContext.Set<TPo>().AddRange(poList);
            return true;
        }

        /// <summary>
        /// 异步批量插入
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="bulkInsert"></param>
        /// <param name="trackCurUser"></param>
        /// <returns></returns>
        public virtual async Task<bool> AddRangeAsync(IEnumerable<TDo> entities, bool bulkInsert = false, bool trackCurUser = true)
        {
            var poList = GetPersistentObjectList(entities.ToList(), trackCurUser == false ? (bool?)null : true);
            if (bulkInsert)
            {
                await _readwriteContext.BulkInsertAsync(poList);
            }
            else
            {
                await _readwriteContext.Set<TPo>().AddRangeAsync(poList);
            }
            return true;
        }

        /// <summary>
        /// 更新对象
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public virtual bool Update(TDo t, bool trackCurUser = true)
        {
            var po = GetPersistentObject(t, trackCurUser == false ? (bool?)null : false);
            var entry = _readwriteContext.Entry(po);
            entry.State = EntityState.Modified;
            entry.NotModifyBaseProperties();
            return true;
        }

        public virtual bool Update(TDo t, bool trackCurUser = true, params Expression<Func<TDo, object>>[] properties)
        {
            var po = GetPersistentObject(t, trackCurUser == false ? (bool?)null : false);
            var entry = _readwriteContext.Entry(po);
            foreach (var selector in properties)
            {
                entry.Property(ReflectionHelper.GetPropertyName(selector)).IsModified = true;
            }
            if (trackCurUser)
            {
                entry.Property(ReflectionHelper.GetPropertyName<TDo>(x => x.UpdateUserId)).IsModified = true;
                entry.Property(ReflectionHelper.GetPropertyName<TDo>(x => x.UpdateUserName)).IsModified = true;
                entry.Property(ReflectionHelper.GetPropertyName<TDo>(x => x.UpdateTime)).IsModified = true;
            }
            return true;
        }

        public virtual bool UpdateRange(List<TDo> list, bool bulkUpdate = false, bool trackCurUser = true)
        {
            if (bulkUpdate)
                _readwriteContext.BulkUpdate(GetPersistentObjectList(list, trackCurUser == false ? (bool?)null : false));
            else
                list.ForEach(x => Update(x, trackCurUser));
            return true;
        }

        public virtual bool UpdateIsDeleted(TDo entity, bool isDel = true, bool trackCurUser = false)
        {
            if (isDel)
            {
                SetDeleteInfo(entity);
            }

            var re = Update(entity, trackCurUser, x => x.IsDeleted, x => x.DelUserName, x => x.DelUserId, x => x.DelTime);
            return re;
        }

        public void SetDeleteInfo(TDo entity)
        {
            entity.IsDeleted = true;
            entity.DelTime = DateTime.Now;
            entity.DelUserId = _currentUserInfo.UserId;
            entity.DelUserName = _currentUserInfo.UserName;
        }

        public void SetDeleteInfo(TPo entity)
        {
            entity.IsDeleted = true;
            entity.DelTime = DateTime.Now;
            entity.DelUserId = _currentUserInfo.UserId;
            entity.DelUserName = _currentUserInfo.UserName;
        }
        public virtual bool UpdateIsDeletedRange(List<TDo> list, bool isDel = true, bool trackCurUser = false)
        {
            list.ForEach(x => UpdateIsDeleted(x, isDel, trackCurUser));
            return true;
        }


        /// <summary>
        /// 删除对象
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public virtual bool Delete(TDo t)
        {
            _readwriteContext.Set<TPo>().Remove(GetPersistentObject(t));
            return true;
        }

        public virtual async Task<bool> DeleteAsync(object key)
        {
            var entity = await this.GetAsync(key);

            if (entity != null)
            {
                _readwriteContext.Set<TPo>().Remove(MapperHelper<TDo, TPo>.Map(entity));
            }
            return true;
        }


        /// <summary>
        /// 根据主键获取对象
        /// </summary>
        /// <returns></returns>
        public virtual async Task<TDo> GetAsync(Guid key, bool readOnly = true)
        {
            var context = readOnly ? _readonlyContext : _readwriteContext;
            var result = await GetAll().SingleOrDefaultAsync(x => x.Id.Equals(key));
            if (result != null)
            {
                return GetDomainEntity(result);
            }
            return null;
        }
        /// <summary>
        /// 根据主键获取对象
        /// </summary>
        /// <returns></returns>
        public virtual async Task<TDo> GetAsync(object key, bool readOnly = true)
        {
            var context = readOnly ? _readonlyContext : _readwriteContext;
            var result = await context.Set<TPo>().FindAsync(key);
            if (result != null)
            {
                context.Set<TPo>().Attach(result).State = EntityState.Detached;
                return GetDomainEntity(result);
            }
            return null;
        }
        /// <summary>
        /// 根据条件查询对象
        /// </summary>
        /// <param name="specification"></param>
        /// <param name="asNoTracking"></param>
        /// <returns></returns>
        public virtual async Task<TDo> GetAsync(Expression<Func<TDo, bool>> specification, bool asNoTracking = true, bool excludeIsDeleted = true, bool readOnly = true)
        {
            return GetDomainEntity(await GetAll(asNoTracking, excludeIsDeleted: excludeIsDeleted, readOnly: readOnly).FirstOrDefaultAsync(specification.ReplaceParameter<TDo, TPo>()));
        }
        public virtual async Task<TDo> GetAsync(ISpecification<TDo> specification, bool asNoTracking = true, bool excludeIsDeleted = true, bool readOnly = true)
        {
            return GetDomainEntity(await GetAll(asNoTracking, excludeIsDeleted: excludeIsDeleted, readOnly: readOnly).FirstOrDefaultAsync(specification.SatisfiedBy().ReplaceParameter<TDo, TPo>()));
        }

        public async Task<IEnumerable<TDo>> GetListByTopNAsync(int TopN, ISpecification<TDo> specification, bool asNoTracking = true, bool excludeIsDeleted = true, bool readOnly = true, params (Expression<Func<TDo, dynamic>>, bool)[] orderbys)
        {
            var query = (await GetManyAsync(specification, asNoTracking, excludeIsDeleted, readOnly: readOnly, orderbys)).Take(TopN);

            return query;
        }

        /// <summary>
        /// 根据条件查询对象集合
        /// </summary>
        /// <param name="specification"></param>
        /// <param name="asNoTracking"></param>
        /// <returns></returns>
        public virtual async Task<List<TDo>> GetManyAsync(ISpecification<TDo> specification, bool asNoTracking = true, bool excludeIsDeleted = true, bool readOnly = true, params (Expression<Func<TDo, dynamic>>, bool)[] orderbys)
        {
            if (orderbys != null && orderbys.Any())
            {
                var query = GetOrderQueryable(GetAll(asNoTracking, !orderbys.Any(), excludeIsDeleted, readOnly: readOnly).Where(specification.SatisfiedBy().ReplaceParameter<TDo, TPo>()), orderbys.ToList());
                return GetDomainEntityList(await query.ToListAsync());
            }
            else
            {
                return GetDomainEntityList(await GetAll(asNoTracking, excludeIsDeleted: excludeIsDeleted, readOnly: readOnly).Where(specification.SatisfiedBy().ReplaceParameter<TDo, TPo>()).ToListAsync());
            }
        }
        public virtual async Task<List<TDo>> GetManyAsync(Expression<Func<TDo, bool>> specification, bool asNoTracking = true, bool excludeIsDeleted = true, bool readOnly = true, params (Expression<Func<TDo, dynamic>>, bool)[] orderbys)
        {
            if (orderbys != null && orderbys.Any())
            {
                var query = GetOrderQueryable(GetAll(asNoTracking, !orderbys.Any(), excludeIsDeleted, readOnly: readOnly).Where(specification.ReplaceParameter<TDo, TPo>()), orderbys.ToList());
                return GetDomainEntityList(await query.ToListAsync());
            }
            else
            {
                return GetDomainEntityList(await GetAll(asNoTracking, !orderbys.Any(), excludeIsDeleted, readOnly: readOnly).Where(specification.ReplaceParameter<TDo, TPo>()).ToListAsync());
            }
        }
        /// <summary>
        /// 根据条件判断对象是否存在
        /// </summary>
        /// <param name="specification"></param>
        /// <param name="asNoTracking"></param>
        /// <returns></returns>
        public virtual async Task<bool> AnyAsync(Expression<Func<TDo, bool>> specification, bool asNoTracking = true, bool excludeIsDeleted = true, bool readOnly = true)
        {
            return await GetAll(asNoTracking, excludeIsDeleted: excludeIsDeleted, readOnly: readOnly).AnyAsync(specification.ReplaceParameter<TDo, TPo>());
        }
        public virtual async Task<bool> AnyAsync(ISpecification<TDo> specification, bool asNoTracking = true, bool excludeIsDeleted = true, bool readOnly = true)
        {
            return await GetAll(asNoTracking, excludeIsDeleted: excludeIsDeleted, readOnly: readOnly).AnyAsync(specification.SatisfiedBy().ReplaceParameter<TDo, TPo>());
        }

        /// <summary>
        /// 根据条件返回对象数量
        /// </summary>
        /// <param name="specification"></param>
        /// <param name="asNoTracking"></param>
        /// <returns></returns>
        public virtual async Task<int> CountAsync(Expression<Func<TDo, bool>> specification, bool asNoTracking = true, bool excludeIsDeleted = true, bool readOnly = true)
        {
            return await GetAll(asNoTracking, excludeIsDeleted: excludeIsDeleted, readOnly: readOnly).CountAsync(specification.ReplaceParameter<TDo, TPo>());
        }
        public virtual async Task<int> CountAsync(ISpecification<TDo> specification, bool asNoTracking = true, bool excludeIsDeleted = true, bool readOnly = true)
        {
            return await GetAll(asNoTracking, excludeIsDeleted: excludeIsDeleted, readOnly: readOnly).CountAsync(specification.SatisfiedBy().ReplaceParameter<TDo, TPo>());
        }
        /// <summary>
        /// 分页
        /// </summary>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="specification"></param>
        /// <param name="orderbys"></param>
        /// <returns></returns>
        public virtual async Task<PageData<TDo>> PageQueryAsync(int pageIndex, int pageSize, ISpecification<TDo> specification, bool excludeIsDeleted = true, bool readOnly = true, params (Expression<Func<TDo, dynamic>>, bool)[] orderbys)
        {
            var query = GetAll(true, !orderbys.Any(), excludeIsDeleted, readOnly: readOnly).Where(specification.SatisfiedBy().ReplaceParameter<TDo, TPo>());
            var result = new PageData<TDo>();
            result.Page = pageIndex;
            result.PageSize = pageSize;
            result.Total = await query.CountAsync();
            if (result.Total > 0)
            {
                result.Rows = GetDomainEntityList(await PageQueryResult(query, pageIndex, pageSize, orderbys));
            }
            else
            {
                result.Rows = new List<TDo>();
            }
            return result;
        }
        public virtual async Task<PageData<TDo>> PageQueryAsync(int pageIndex, int pageSize, Expression<Func<TDo, bool>> specification, bool excludeIsDeleted = true, bool readOnly = true, params (Expression<Func<TDo, dynamic>>, bool)[] orderbys)
        {
            var query = GetAll(true, !orderbys.Any(), excludeIsDeleted, readOnly: readOnly).Where(specification.ReplaceParameter<TDo, TPo>());
            var result = new PageData<TDo>();
            result.Page = pageIndex;
            result.PageSize = pageSize;
            result.Total = await query.CountAsync();
            if (result.Total > 0)
            {
                result.Rows = GetDomainEntityList(await PageQueryResult(query, pageIndex, pageSize, orderbys));
            }
            else
            {
                result.Rows = new List<TDo>();
            }
            return result;
        }
        /// <summary>
        /// 异步工作单元
        /// </summary>
        /// <returns></returns>
        public virtual async Task<bool> SaveAsync()
        {
            await _readwriteContext.SaveChangesAsync();
            return true;
        }

        public IEnumerable<TDo> GetWithRawSql(string query, bool readOnly = true, params object[] parameters)
        {
            var context = readOnly ? _readonlyContext : _readwriteContext;
            var list =  //_context.Set<TPo>().FromSql(query, parameters);
            context.Set<TPo>().FromSqlRaw(query, parameters); //ef3.0
            return MapperHelper<TPo, TDo>.MapList(list);
        }

        public async Task<List<TDo>> GetWithRawSqlAsync(string query, bool readOnly = true, params object[] parameters)
        {
            var context = readOnly ? _readonlyContext : _readwriteContext;
            var list =    await context.Set<TPo>().FromSqlRaw(query, parameters).ToListAsync(); //ef3.0
            return MapperHelper<TPo, TDo>.MapList(list);
        }

        public async Task<int> ExecuteSqlCommandAsync(string query, params object[] parameters)
        {
            return   await _readwriteContext.Database.ExecuteSqlRawAsync(query, parameters);//ef3.0
        }


        #region base methods

        /// <summary>
        /// 强制重写转换类用于查询
        /// </summary>
        /// <param name="asNoTracking"></param>
        /// <param name="excludeIsDeleted">不包含逻辑删除的记录</param>
        /// <returns></returns>
        public virtual IQueryable<TPo> GetAll(bool asNoTracking = true, bool defaultOrderBy = true, bool excludeIsDeleted = true, bool readOnly = true)
        {
            var context = readOnly ? _readonlyContext : _readwriteContext;
            if (!asNoTracking)
            {
                if (!defaultOrderBy)
                {
                    return excludeIsDeleted ? context.Set<TPo>().Where(x => !x.IsDeleted)
                        : context.Set<TPo>();
                }
                else
                {
                    return excludeIsDeleted ? context.Set<TPo>().Where(x => !x.IsDeleted).OrderByDescending(x => x.CreateTime)
                        : context.Set<TPo>().OrderByDescending(x => x.CreateTime);
                }
            }
            else
            {
                if (!defaultOrderBy)
                {
                    return excludeIsDeleted ? context.Set<TPo>().AsNoTracking().Where(x => !x.IsDeleted)
                        : context.Set<TPo>().AsNoTracking();
                }
                else
                {
                    return excludeIsDeleted ? context.Set<TPo>().AsNoTracking().Where(x => !x.IsDeleted).OrderByDescending(x => x.CreateTime)
                        : context.Set<TPo>().AsNoTracking().OrderByDescending(x => x.CreateTime);
                }
            }
        }
        /// <summary>
        /// 通用泛型方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="asNoTracking"></param>
        /// <param name="defaultOrderBy"></param>
        /// <param name="excludeIsDeleted"></param>
        /// <param name="readOnly"></param>
        /// <returns></returns>
        public virtual IQueryable<T> GetAll<T>(bool asNoTracking = true, bool defaultOrderBy = false, bool excludeIsDeleted = true, bool readOnly = true) where T : PersistenceObjectBase, new()
        {
            var context = readOnly ? _readonlyContext : _readwriteContext;
            if (!asNoTracking)
            {
                if (!defaultOrderBy)
                {
                    return excludeIsDeleted ? context.Set<T>().Where(x => !x.IsDeleted)
                        : context.Set<T>();
                }
                else
                {
                    return excludeIsDeleted ? context.Set<T>().Where(x => !x.IsDeleted).OrderByDescending(x => x.CreateTime)
                        : context.Set<T>().OrderByDescending(x => x.CreateTime);
                }
            }
            else
            {
                if (!defaultOrderBy)
                {
                    return excludeIsDeleted ? context.Set<T>().AsNoTracking().Where(x => !x.IsDeleted)
                        : context.Set<T>().AsNoTracking();
                }
                else
                {
                    return excludeIsDeleted ? context.Set<T>().AsNoTracking().Where(x => !x.IsDeleted).OrderByDescending(x => x.CreateTime)
                        : context.Set<T>().AsNoTracking().OrderByDescending(x => x.CreateTime);
                }
            }
        }
        async Task<List<TPo>> PageQueryResult(IQueryable<TPo> query, int pageIndex, int pageSize, params (Expression<Func<TDo, dynamic>>, bool)[] orderbys)
        {
            if (orderbys != null && orderbys.Any())
            {
                query = GetOrderQueryable(query, orderbys.ToList());
            }
            return await (query.Skip((pageIndex - 1) * pageSize).Take(pageSize)).ToListAsync();
        }
        IOrderedQueryable<TPo> GetOrderQueryable(IQueryable<TPo> source, List<(Expression<Func<TDo, dynamic>>, bool)> orderbyList)
        {
            orderbyList.ForEach(x =>
            {
                var newOrder = x.Item1.ReplaceParameter<TDo, TPo>();
                source = (IQueryable<TPo>)source.Provider.CreateQuery(Expression.Call(typeof(Queryable),
                    orderbyList.IndexOf(x) == 0 && x.Item2 ?
                    nameof(OrderEnum.OrderBy) :
                    orderbyList.IndexOf(x) == 0 && !x.Item2 ?
                    nameof(OrderEnum.OrderByDescending) :
                    orderbyList.IndexOf(x) > 0 && x.Item2 ?
                    nameof(OrderEnum.ThenBy) : nameof(OrderEnum.ThenByDescending)
                    , new[] { typeof(TPo), typeof(object) }, source.Expression, newOrder));
            });
            return (IOrderedQueryable<TPo>)source;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="trackAddUpdateUser">Add true  Update false</param>
        /// <returns></returns>
        public TPo GetPersistentObject(TDo source, bool? trackAddUpdateUser = null)
        {
            var result = MapperHelper<TDo, TPo>.Map(source);
            TrackAddUpdateUser(trackAddUpdateUser, result);
            return result;
        }

        public void TrackAddUpdateUser<T>(bool? trackAddUpdateUser, T result)
            where T : PersistenceObjectBase, new()
        {
            if (trackAddUpdateUser.HasValue && trackAddUpdateUser.Value)
            {
                result.CreateUserId = _currentUserInfo.UserId;
                result.CreateUserName = _currentUserInfo.UserName;
                result.CreateTime = DateTime.Now;
            }
            else if (trackAddUpdateUser.HasValue && !trackAddUpdateUser.Value)
            {
                result.UpdateUserId = _currentUserInfo.UserId;
                result.UpdateUserName = _currentUserInfo.UserName;
                result.UpdateTime = DateTime.Now;
            }
        }

        public List<TPo> GetPersistentObjectList(ICollection<TDo> source, bool? trackAddUpdateUser = null)
        {
            var result = MapperHelper<TDo, TPo>.MapList(source);
            BatchTrackAddUpdateUser(trackAddUpdateUser, result);
            return result;
        }

        public void BatchTrackAddUpdateUser<T>(bool? trackAddUpdateUser, List<T> result)
            where T : PersistenceObjectBase, new()
        {
            if (trackAddUpdateUser.HasValue && trackAddUpdateUser.Value)
            {
                result.ForEach(x =>
                {
                    x.CreateUserId = _currentUserInfo.UserId;
                    x.CreateUserName = _currentUserInfo.UserName;
                    x.CreateTime = DateTime.Now;
                }
                );

            }
            else if (trackAddUpdateUser.HasValue && !trackAddUpdateUser.Value)
            {
                result.ForEach(x =>
                {
                    x.UpdateUserId = _currentUserInfo.UserId;
                    x.UpdateUserName = _currentUserInfo.UserName;
                    x.UpdateTime = DateTime.Now;
                }
                );
            }
        }

        public TDo GetDomainEntity(TPo source)
        {
            return MapperHelper<TPo, TDo>.Map(source);
        }
        public List<TDo> GetDomainEntityList(ICollection<TPo> source)
        {
            return MapperHelper<TPo, TDo>.MapList(source);
        }
        public TOut GetPersistentObject<TIn, TOut>(TIn source, bool? trackAddUpdateUser = null) where TIn : Entity, new() where TOut : PersistenceObjectBase
        {
            var result = MapperHelper<TIn, TOut>.Map(source);
            if (trackAddUpdateUser.HasValue && trackAddUpdateUser.Value)
            {
                result.CreateUserId = _currentUserInfo.UserId;
                result.CreateUserName = _currentUserInfo.UserName;
                result.CreateTime = DateTime.Now;
            }
            else if (trackAddUpdateUser.HasValue && !trackAddUpdateUser.Value)
            {
                result.UpdateUserId = _currentUserInfo.UserId;
                result.UpdateUserName = _currentUserInfo.UserName;
                result.UpdateTime = DateTime.Now;
            }
            return result;
        }
        public List<TOut> GetPersistentObjectList<TIn, TOut>(ICollection<TIn> source, bool? trackAddUpdateUser = null) where TIn : Entity, new() where TOut : PersistenceObjectBase
        {
            var result = MapperHelper<TIn, TOut>.MapList(source);
            if (trackAddUpdateUser.HasValue && trackAddUpdateUser.Value)
            {
                result.ForEach(x =>
                {
                    x.CreateUserId = _currentUserInfo.UserId;
                    x.CreateUserName = _currentUserInfo.UserName;
                    x.CreateTime = DateTime.Now;
                }
                );

            }
            else if (trackAddUpdateUser.HasValue && !trackAddUpdateUser.Value)
            {
                result.ForEach(x =>
                {
                    x.UpdateUserId = _currentUserInfo.UserId;
                    x.UpdateUserName = _currentUserInfo.UserName;
                    x.UpdateTime = DateTime.Now;
                }
                );
            }
            return result;
        }
        public TOut GetDomainEntity<TIn, TOut>(TIn source) where TIn : PersistenceObjectBase, new() where TOut : Entity
        {
            return MapperHelper<TIn, TOut>.Map(source);
        }
        public List<TOut> GetDomainEntityList<TIn, TOut>(ICollection<TIn> source) where TIn : PersistenceObjectBase, new() where TOut : Entity
        {
            return MapperHelper<TIn,TOut>.MapList(source);
        }
        #endregion
    }
}
